#pragma once
#include "spdlog/fmt/ranges.h"
#include "spdlog/spdlog.h"
#include <memory>

#define LOG_HELPER(LogLevel, ...) \
    zh::Logger::getInstance().log( \
        LogLevel, "[" + std::string(__FUNCTION__) + ":" + std::to_string(__LINE__) + "] " + __VA_ARGS__);

#define LOG_DEBUG(...) LOG_HELPER(zh::Logger::LogLevel::DEBUG, __VA_ARGS__);
#define LOG_INFO(...) LOG_HELPER(zh::Logger::LogLevel::INFO, __VA_ARGS__);
#define LOG_WARN(...) LOG_HELPER(zh::Logger::LogLevel::WARN, __VA_ARGS__);
#define LOG_ERROR(...) LOG_HELPER(zh::Logger::LogLevel::ERROR, __VA_ARGS__);
#define LOG_FATAL(...) LOG_HELPER(zh::Logger::LogLevel::FATAL, __VA_ARGS__);

namespace zh
{
    class Logger
    {
    public:
        static Logger& getInstance();

        enum class LogLevel
        {
            DEBUG,
            INFO,
            WARN,
            ERROR,
            FATAL
        };

        template<typename... Args>
        void log(LogLevel level, Args&&... args)
        {
            switch (level)
            {
                case LogLevel::DEBUG:
                    m_logger->debug(std::forward<Args>(args)...);
                    break;
                case LogLevel::INFO:
                    m_logger->info(std::forward<Args>(args)...);
                    break;
                case LogLevel::WARN:
                    m_logger->warn(std::forward<Args>(args)...);
                    break;
                case LogLevel::ERROR:
                    m_logger->error(std::forward<Args>(args)...);
                    break;
                case LogLevel::FATAL:
                    m_logger->critical(std::forward<Args>(args)...);
                    break;
            }
        }

    private:
        Logger();
        ~Logger();

        std::shared_ptr<spdlog::logger> m_logger;
    };
} // namespace zh